package com.train.daily.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.train.common.enums.impl.BizExceptionEnums;
import com.train.common.exception.GlobalException;
import com.train.common.resp.CommonResp;
import com.train.common.resp.PageResp;
import com.train.common.utils.exception.ExceptionUtils;
import com.train.daily.dto.train.*;
import com.train.daily.mapper.DailyTrainMapper;
import com.train.daily.po.DailyTrainPO;
import com.train.daily.resp.train.DailyTrainPageResp;
import com.train.daily.resp.train.DailyTrainQueryResp;
import com.train.daily.service.*;
import com.train.openfeign.core.utils.OpenFeignUtils;
import com.train.system.api.TrainApi;
import com.train.system.dto.train.TrainPageDTO;
import com.train.system.resp.train.TrainPageResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author baitiaojun
 * @description 针对表【dailyTrain(每日车次)】的数据库操作Service实现
 * @createDate 2025-02-28 17:55:24
 */
@Slf4j
@Service
public class DailyTrainServiceImpl extends ServiceImpl<DailyTrainMapper, DailyTrainPO>
    implements DailyTrainService {

    private final DailyTrainMapper dailyTrainMapper;

    private final TrainApi trainApi;

    private final DailyTrainCarriageService dailyTrainCarriageService;

    private final DailyTrainSeatService dailyTrainSeatService;

    private final DailyTrainStationService dailyTrainStationService;

    private final DailyTrainTicketService dailyTrainTicketService;

    private final ThreadPoolExecutor threadPoolExecutor;

    private final PlatformTransactionManager transactionManager;

    private final TransactionDefinition transactionDefinition;

    private final DailyTrainSkTokenService dailyTrainSkTokenService;

    public DailyTrainServiceImpl(DailyTrainMapper dailyTrainMapper,
                                 @Qualifier("com.train.system.api.TrainApi") TrainApi trainApi,
                                 DailyTrainCarriageService dailyTrainCarriageService,
                                 DailyTrainSeatService dailyTrainSeatService,
                                 DailyTrainStationService dailyTrainStationService,
                                 DailyTrainTicketService dailyTrainTicketService,
                                 ThreadPoolExecutor threadPoolExecutor,
                                 PlatformTransactionManager transactionManager,
                                 TransactionDefinition transactionDefinition,
                                 DailyTrainSkTokenService dailyTrainSkTokenService) {
        this.dailyTrainMapper = dailyTrainMapper;
        this.trainApi = trainApi;
        this.dailyTrainCarriageService = dailyTrainCarriageService;
        this.dailyTrainSeatService = dailyTrainSeatService;
        this.dailyTrainStationService = dailyTrainStationService;
        this.dailyTrainTicketService = dailyTrainTicketService;
        this.threadPoolExecutor = threadPoolExecutor;
        this.transactionManager = transactionManager;
        this.transactionDefinition = transactionDefinition;
        this.dailyTrainSkTokenService = dailyTrainSkTokenService;
    }

    @Override
    public void addOrEditDailyTrain(DailyTrainAddOrEditDTO dailyTrainAddOrEditDTO) {
        //校验车次是否存在
        CommonResp resp = trainApi.validTrainCodeList(dailyTrainAddOrEditDTO.getCode());
        if (!OpenFeignUtils.isSuccess(resp)) {
            throw new GlobalException(BizExceptionEnums.EXP_TRAIN_CODE_NOT_EXISTS);
        }

        //校验id是否存在,判断是insert还是update
        if (dailyTrainAddOrEditDTO.getId() != null) {
            //如果是编辑,需要校验id记录是否存在
            if (!dailyTrainMapper.existsId(dailyTrainAddOrEditDTO.getId())) {
                throw new GlobalException(BizExceptionEnums.EXP_DAILY_TRAIN_NOT_EXISTS);
            }

            //获取当日所有车次记录
            List<DailyTrainCodeDTO> dailyTrains = dailyTrainMapper.getDailyTrainCode(dailyTrainAddOrEditDTO.getDate());

            //获取除开更新车次的其他当日车次
            List<String> dailyTrainCodes = dailyTrains.stream()
                    .filter(dailyTrainCodeDTO -> !dailyTrainCodeDTO.getId().equals(dailyTrainAddOrEditDTO.getId()))
                    .map(DailyTrainCodeDTO::getCode).toList();

            //更新车次与该日的其他车次比较,判断是否已存在相同车次
            if (dailyTrainCodes.contains(dailyTrainAddOrEditDTO.getCode())) {
                throw new GlobalException(BizExceptionEnums.EXP_DAILY_TRAIN_CODE_EXISTS);
            }
        } else {
            //如果是新增,需要校验车次是否存在
            if (dailyTrainMapper.existsDailyTrain(dailyTrainAddOrEditDTO.getCode(), dailyTrainAddOrEditDTO.getDate())) {
                throw new GlobalException(BizExceptionEnums.EXP_DAILY_TRAIN_CODE_EXISTS);
            }
        }

        DailyTrainPO dailyTrainPO = BeanUtil.copyProperties(dailyTrainAddOrEditDTO, DailyTrainPO.class);
        boolean isSaved = this.saveOrUpdate(dailyTrainPO);
        if (!isSaved) {
            throw new GlobalException(BizExceptionEnums.EXP_ADD_DAILY_TRAIN);
        }
    }

    @Override
    public PageResp<DailyTrainPageResp> pageList(DailyTrainPageDTO dailyTrainPageDTO) {
        return dailyTrainMapper.pageList(dailyTrainPageDTO);
    }

    @Override
    public void remove(Long id) {
        boolean isRemoved = this.removeById(id);
        if (!isRemoved) {
            throw new GlobalException(BizExceptionEnums.EXP_REMOVE_DAILY_TRAIN);
        }
    }

    /**
     * 异步生成每日车次数据
     * @param date
     */
    public void generateDailyTrain(String date) {
        threadPoolExecutor.execute(() -> {
            TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
            try {
                Long pageNo  = 1L;
                Long pageSize = 10L;
                while (true) {
                    //查询车次基础数据
                    CommonResp resp = trainApi.getTrainList(new TrainPageDTO(pageNo, pageSize));

                    //解析结果
                    List<TrainPageResp> trainList = OpenFeignUtils.getResultList(resp, TrainPageResp.class);
                    if (trainList.isEmpty()) {
                        break;
                    }

                    //得到每日的车次code
                    List<String> trainCodes = trainList.stream().map(TrainPageResp::getCode).toList();

                    //生成每日车次
                    this.generateDailyTrain(date, trainList, trainCodes);

                    //生成车厢
                    dailyTrainCarriageService.generateDailyCarriage(date, trainCodes);

                    //生成车站
                    dailyTrainStationService.generateDailyTrainStation(date, trainCodes);

                    //生成座位
                    dailyTrainSeatService.generateDailySeat(date, trainCodes);

                    //生成车票
                    dailyTrainTicketService.generateDailyTrainTicket(date, trainCodes);

                    //生成每日令牌
                    dailyTrainSkTokenService.generateDailyTrainSkToken(date, trainCodes);

                    //查询下一页
                    pageNo++;
                }
                transactionManager.commit(transactionStatus);
            } catch (Exception e) {
                log.error("生成每日车次失败,原因:{}", ExceptionUtils.getExpMsg(e));
                transactionManager.rollback(transactionStatus);
            }
        });
    }

    @Override
    public void existsTrain(DailyTrainExistsDTO dto) {
        boolean exists = dailyTrainMapper.existsDailyTrain(dto.getTrainCode(), dto.getDate());
        if (!exists) {
            throw new GlobalException(BizExceptionEnums.EXP_DAILY_TRAIN_NOT_EXISTS);
        }
    }

    @Override
    public void validTrainTime(DailyTrainValidationDTO dto) {
        LocalTime startTime = dailyTrainMapper.getDailyTrain(dto.getDate(), dto.getTrainCode());
        //如果24小时之内当前时间大于或等于发车时间,就返回异常
        if (LocalDate.now().equals(dto.getDate())) {
            LocalTime now = LocalTime.now();
            if (startTime != null && (startTime.isBefore(now) || startTime.equals(now))) {
                throw new GlobalException(BizExceptionEnums.EXP_DAILY_TRAIN_START_TIME_ERROR);
            }
        }
    }

    @Override
    public DailyTrainQueryResp queryTrainByTrainCodeDate(String trainCode, String date) {
        return dailyTrainMapper.queryTrain(trainCode, LocalDate.parse(date));
    }

    private void generateDailyTrain(String date, List<TrainPageResp> trainList, List<String> trainCodes) {
        //删除当天车次
        if (dailyTrainMapper.existsDailyTrain(LocalDate.parse(date), trainCodes)) {
            boolean isDeleted = dailyTrainMapper.deleteTrains(LocalDate.parse(date), trainCodes);
            if (!isDeleted) {
                throw new GlobalException(BizExceptionEnums.EXP_ADD_DAILY_TRAIN_TICKET);
            }
        }

        //重新添加当天车次
        List<DailyTrainPO> dailyTrainPOS = BeanUtil.copyToList(trainList, DailyTrainPO.class);
        dailyTrainPOS.forEach(dailyTrainPO -> dailyTrainPO.setDate(LocalDate.parse(date)));
        boolean isSaved = this.saveBatch(dailyTrainPOS);
        if (!isSaved) {
            throw new GlobalException(BizExceptionEnums.EXP_ADD_DAILY_TRAIN);
        }
    }
}




